package dao

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"time"

	"bmember/internal/object"

	"bmember/pkg/util"

	"bmember/internal/model"
	"bmember/pkg/db/pgex"
)

// MemberDao 会员相关 dao 层
type MemberDao interface {
	// GetMemberById 根据 id 查询会员
	GetMemberById(ctx context.Context, id int64) (*model.Member, error)
	// LockMemberById 锁定会员
	LockMemberById(ctx context.Context, id int64) (*model.Member, error)
	// GetMemberByMobile 根据手机号获取会员
	GetMemberByMobile(ctx context.Context, brandId int64, mobile string) (*model.Member, error)
	// CreateMember 创建会员
	CreateMember(ctx context.Context, mem *model.Member) (int64, error)
	// UpdateMemberById 更新用户基本信息
	UpdateMemberById(ctx context.Context, mem *model.Member) error
	// UpdateMemberFormalTag 设置会员 formal tag
	UpdateMemberFormalTag(ctx context.Context, mem *model.Member) error
	// UpdateMobileEmail 更新会员手机号邮箱
	UpdateMobileEmail(ctx context.Context, mem *model.Member) error
	// DeleteMemberById 删除会员
	DeleteMemberById(ctx context.Context, mem *model.Member) error
	// SearchMemberByMobileNickname 用户名手机号检索
	SearchMemberByMobileNickname(ctx context.Context, brandId int64, mobile, nickname string) ([]*model.Member, error)
	// GetMobileNicknameByIds 根据ids 获取用户昵称，手机号
	GetMobileNicknameByIds(ctx context.Context, ids []int64) ([]*model.Member, error)
	// SearchMembersForAdmin 管理后台用户搜索
	SearchMembersForAdmin(ctx context.Context, brandId, cardId, memberId int64, nickname, mobile string, pg object.Pagination) ([]*object.AdminMemberForList, uint32, error)
	// GetAdminMemberStatisticCache 后台获取用户统计缓存
	GetAdminMemberStatisticCache(ctx context.Context, brandId int64) (*object.AdminMemberStatistic, error)
	// CacheAdminMemberStatistic 设置后台统计缓存
	CacheAdminMemberStatistic(ctx context.Context, statistic *object.AdminMemberStatistic) error
	// CountMembersByCreateTime 计算用户数量
	CountMembersByCreateTime(ctx context.Context, brandId int64, start, end time.Time) (uint32, error)
}

type mDaoImpl struct {
	*DaoBase
	name string
}

var mDao MemberDao

// InitMemberDao 初始化 会员相关 dao 层
func InitMemberDao() {
	mDao = &mDaoImpl{
		DaoBase: daoBase,
		name:    "dao_group",
	}
}

// GetMemberDao 获取 menber dao
func GetMemberDao() MemberDao {
	return mDao
}

// GetMemberById 根据 id 查询会员
func (dao *mDaoImpl) GetMemberById(ctx context.Context, id int64) (*model.Member, error) {
	query := "SELECT * FROM member WHERE id=? AND deleted_at=0"
	m := &model.Member{}
	err := dao.db.Get(ctx, m, query, id)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return m, err
}

// LockMemberById 锁定会员
func (dao *mDaoImpl) LockMemberById(ctx context.Context, id int64) (*model.Member, error) {
	query := "SELECT * FROM member WHERE id=? AND deleted_at=0 FOR UPDATE"
	m := &model.Member{}
	err := dao.db.Get(ctx, m, query, id)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return m, err
}

// GetMemberByMobile 根据手机号获取会员
func (dao *mDaoImpl) GetMemberByMobile(ctx context.Context, brandId int64, mobile string) (*model.Member, error) {
	if mobile == "" {
		return nil, nil
	}
	query := "SELECT * FROM member WHERE brand_id=? AND mobile=? AND deleted_at=0"
	member := &model.Member{}
	err := dao.db.Get(ctx, member, query, brandId, mobile)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return member, err
}

// CreateMember 创建会员
func (dao *mDaoImpl) CreateMember(ctx context.Context, mem *model.Member) (int64, error) {
	if mem == nil {
		return 0, nil
	}
	param := []interface{}{
		mem.BrandId,
		mem.Nickname,
		mem.Mobile,
		mem.Email,
		mem.Gender,
		mem.Avatar,
		mem.Birthday,
		mem.CountryCode,
		mem.Province,
		mem.City,
		mem.RegOrigin,
		mem.LoginPasswd,
		mem.IsFormal,
		mem.Status,
		mem.Remark,
		mem.CreatedAt,
		mem.UpdatedAt,
	}
	query := "INSERT INTO member (brand_id, nickname, mobile, email, gender, avatar, birthday, country_code, " +
		"province, city, reg_origin, login_passwd, is_formal, status, remark, created_at, updated_at) " +
		"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, _ := aff.GetLastId()
	return lastId, nil
}

// UpdateMemberById 更新用户基本信息
func (dao *mDaoImpl) UpdateMemberById(ctx context.Context, mem *model.Member) error {
	if mem == nil {
		return nil
	}
	param := []interface{}{
		mem.Nickname,
		mem.Avatar,
		mem.CountryCode,
		mem.Gender,
		mem.Birthday,
		mem.Province,
		mem.City,
		mem.Remark,
		mem.Id,
	}
	query := "UPDATE member SET  nickname=?, avatar=?, country_code=?, gender=?, birthday=?, province=?, " +
		"city=?, remark=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, param...)
	return err
}

// UpdateMemberFormalTag 设置会员 formal tag
func (dao *mDaoImpl) UpdateMemberFormalTag(ctx context.Context, mem *model.Member) error {
	query := "UPDATE member SET is_formal=?, updated_at=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, mem.IsFormal, mem.UpdatedAt, mem.Id)
	return err
}

// UpdateMobileEmail 更新会员手机号邮箱
func (dao *mDaoImpl) UpdateMobileEmail(ctx context.Context, mem *model.Member) error {
	if mem == nil {
		return nil
	}
	query := "UPDATE member SET mobile=?, email=?, updated_at=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, mem.Mobile, mem.Email, mem.UpdatedAt, mem.Id)
	return err
}

// DeleteMemberById 删除会员
func (dao *mDaoImpl) DeleteMemberById(ctx context.Context, mem *model.Member) error {
	query := "UPDATE member SET  deleted_at=?, status=? WHERE id=? AND deleted_at=0"
	_, err := dao.db.Delete(ctx, query, util.GetCurrentTimestamp(), mem.Status, mem.Id)
	return err
}

// SearchMemberByMobileNickname 用户名手机号检索
func (dao *mDaoImpl) SearchMemberByMobileNickname(ctx context.Context, brandId int64, mobile, nickname string) ([]*model.Member, error) {
	if mobile == "" && nickname == "" {
		return nil, nil
	}
	query := "SELECT id, brand_id, nickname, mobile FROM member WHERE deleted_at=0 AND brand_id=? "
	if mobile != "" {
		query += fmt.Sprintf(" AND mobile='%s'", mobile)
	}
	if nickname != "" {
		query += fmt.Sprintf(" AND nickname LIKE '%%%s%%'", nickname)
	}
	var mems []*model.Member
	err := dao.db.Query(ctx, &mems, query, brandId)
	return mems, err
}

// GetMobileNicknameByIds 根据ids 获取用户昵称，手机号
func (dao *mDaoImpl) GetMobileNicknameByIds(ctx context.Context, ids []int64) ([]*model.Member, error) {
	if len(ids) == 0 {
		return nil, nil
	}
	query := "SELECT id, brand_id, nickname, mobile FROM member WHERE deleted_at=0 AND id IN ? "
	var mems []*model.Member
	err := dao.db.Query(ctx, &mems, query, ids)
	return mems, err
}

// SearchMembersForAdmin 管理后台用户搜索
func (dao *mDaoImpl) SearchMembersForAdmin(ctx context.Context, brandId, cardId, memberId int64, nickname, mobile string, pg object.Pagination) ([]*object.AdminMemberForList, uint32, error) {
	fields := "SELECT m.id AS member_id, m.brand_id, nickname, mobile, COALESCE(a.id, 0) AS acc_id, COALESCE(a.card_id, 0) AS card_id, " +
		"COALESCE(a.balance, 0) AS balance, COALESCE(a.integral, 0) AS integral, m.created_at "
	where := fmt.Sprintf("FROM member m LEFT OUTER JOIN member_account a ON m.id=a.member_id WHERE m.deleted_at=0 AND (a.deleted_at IS NULL OR a.deleted_at=0) AND m.brand_id=%d ", brandId)
	if cardId != 0 {
		where += fmt.Sprintf(" AND a.card_id=%d", cardId)
	}
	if memberId != 0 {
		where += fmt.Sprintf(" AND m.id=%d", memberId)
	}
	if nickname != "" {
		where += fmt.Sprintf(" AND m.nickname LIKE '%%%s%%'", nickname)
	}
	if mobile != "" {
		where += fmt.Sprintf(" AND m.mobile='%d'", mobile)
	}
	total, err := dao.CountNum(ctx, where)
	if err != nil {
		return nil, 0, err
	}
	var mems []*object.AdminMemberForList
	query := fmt.Sprintf("%s %s %s", fields, where, pg.GetDbOffset())
	err = dao.db.Query(ctx, &mems, query)
	return mems, total, err
}

// GetAdminMemberStatisticCache 后台获取用户统计缓存
func (dao *mDaoImpl) GetAdminMemberStatisticCache(ctx context.Context, brandId int64) (*object.AdminMemberStatistic, error) {
	key := dao.buildCacheKey("brand_statistic", strconv.FormatInt(brandId, 10))
	res, err := dao.cache.Get(ctx, key)
	if err != nil {
		if isNil(err) {
			return nil, nil
		}
		return nil, err
	}
	data := &object.AdminMemberStatistic{}
	err = json.Unmarshal([]byte(res), data)
	return data, err
}

// CacheAdminMemberStatistic 设置后台统计缓存
func (dao *mDaoImpl) CacheAdminMemberStatistic(ctx context.Context, statistic *object.AdminMemberStatistic) error {
	if statistic == nil {
		return nil
	}
	key := dao.buildCacheKey("brand_statistic", strconv.FormatInt(statistic.BrandId, 10))
	bS, err := json.Marshal(statistic)
	if err != nil {
		return err
	}
	_, err = dao.cache.Set(ctx, key, bS, util.TodayRemainDuration())
	return err
}

// CountMembersByCreateTime 计算用户数量
func (dao *mDaoImpl) CountMembersByCreateTime(ctx context.Context, brandId int64, start, end time.Time) (uint32, error) {
	query := "SELECT COUNT(*) FROM member WHERE brand_id=? AND deleted_at=0"
	if !util.IsZeroTime(start) {
		query += fmt.Sprintf(" AND created_at>='%s'", util.TimeStdFormat(start))
	}
	if !util.IsZeroTime(end) {
		query += fmt.Sprintf(" AND created_at<'%s'", util.TimeStdFormat(end))
	}
	var cnt uint32
	err := dao.db.Get(ctx, &cnt, query, brandId)
	return cnt, err
}
